home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
assemblr
/
library
/
sampler0
/
cutpaste.asm
< prev
next >
Wrap
Assembly Source File
|
1989-06-17
|
39KB
|
1,205 lines
;------------------------------------------------------------------------------
; CUTPASTE.ASM
; Must be converted to a .COM file
;------------------------------------------------------------------------------
;
;---------------
;======= Equates
;---------------
;
;------- Window
WNDW_HT EQU 25
WNDW_WD EQU 80
;------- Scan codes -- hot keys
CUT_KEY EQU 7000H ; Alt-Fn9
PASTE_KEY EQU 6600H ; Ctl-Fn9
;------- Scan code -- mark key
MARK_CHAR EQU 3B00H ; Fn1
;------- Scan codes -- cursor movement keys
UP_ARROW EQU 4800H
DOWN_ARROW EQU 5000H
LEFT_ARROW EQU 4B00H
RIGHT_ARROW EQU 4D00H
CTL_RGT_ARW EQU 7400H ; Control-RightArrow
CTL_LFT_ARW EQU 7300H ; Control-LeftArrow
HOME_KEY EQU 4700H
END_KEY EQU 4F00H
TOP_PAGE EQU 4900H ; PageUp
BOT_PAGE EQU 5100H ; PageDown
;------- Scan codes -- insert/delete keys
CHAR_INS EQU 5200H
DELETE EQU 5300H ; Delete (lower right)
BACKSPACE EQU 0008H ; User rubout (^H) key
LINE_INS EQU 000DH ; Enter
DEL_TO_EOL EQU 4000H ; Fn6
LINE_DEL EQU 4100H ; Fn7
;------- Scan codes -- carriage return
CAR_RET1 EQU 000DH ; Carriage return (one byte)
CAR_RET2 EQU 1C0DH ; Carriage return (two bytes)
;------- Characters and attributes
BLANK EQU 0720H ; Define the blank character
ATTR EQU 07H ; Default attribute (white on black)
INV_ATTR EQU 70H ; Inverse of ATTR (black on white)
;
;
;======================================= Segment CODESEG
;
COMSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:COMSEG, DS:COMSEG, ES:COMSEG
ORG 100H
START LABEL NEAR
JMP INSTALL
;-------------------------
;======= Data storage area
;-------------------------
;
;------- Copyright information
COPYRITE DB 'Copyright Gerry Boyd, Larry Weiss, Randy Davis 1985 All Rights Reserved'
DB '(214)238-9545'
;------- Buffer to hold screen contents
SCRNSAVE DW ( WNDW_HT * (WNDW_WD+1) ) DUP(BLANK)
;------- Cursor location
CURSOR_POS DW 0
OLD_CUR_POS DW 0
;------- Old stack location
SAVSTAKSP DW 0
SAVSTAKSS DW 0
;------- Request type
REQUEST LABEL WORD
REQUEST0 DB 0
REQUEST1 DB 0
REQUEST2 DB 0
;------- Original keyboard request handler address
OLDINT16 LABEL DWORD
OLDINT16IP DW 0
OLDINT16CS DW 0
;------- Define the confines of the window
LEFT_MARG DB 0
RIGHT_MARG DB 0
TOP_MARG DB 0
BOT_MARG DB 0
;------- Feed chars to application
MARK DW -1 ; Flag used in character feed
FEED_START DW 0 ; Address in buffer of begin...
FEED_STOP DW 0 ; ...and end of feed
FEED_END1 DW 0 ; Temp holding spot for beg feed addr
FEED_END2 DW 0 ; Temp holding spot for end feed addr
FEED_CHAR DW 0 ; Char to feed on this call
;------- Display segment
DISPLAY_SEG DW 0
;------- Flag decribing movement of data between screen and buffer
SWAP_SAVE DB 0
;-------------------
;======= Subroutines
;-------------------
ASSUME CS:COMSEG, DS:COMSEG, ES:NOTHING
;======= Proc CALC_WINDOW
;
; Calculate window extremeties and store them in
; RIGHT_MARG, LEFT_MARG, TOP_MARG, BOT_MARG
;
CALC_WINDOW PROC NEAR
;------- Find out video mode for calculating window size
MOV AH,0FH
INT 10H
;------- Save the video segment
MOV CX,0B000H ; Assume monochrome
CMP AL,7 ; Look for mode 7
JZ CW100 ; Loop around if mono
MOV CX,0B800H ; No, it's graphics
CW100: MOV DISPLAY_SEG,CX
;------- Save window margins
DEC AH ; Number of screen columns
MOV RIGHT_MARG,AH
MOV LEFT_MARG,0
MOV TOP_MARG,0
MOV BOT_MARG,WNDW_HT-1
;------- Exit
RET
CALC_WINDOW ENDP
;======= Proc WINDOW_SAVE
WINDOW_SAVE PROC NEAR
MOV SWAP_SAVE,0
CALL WIN_SWP_SAV
RET
WINDOW_SAVE ENDP
;======= Proc WINDOW_SWAP
WINDOW_SWAP PROC NEAR
MOV SWAP_SAVE,0FFH
CALL WIN_SWP_SAV
RET
WINDOW_SWAP ENDP
;======= Proc WIN_SWP_SAV
;
; When SWAP_SAVE=0, saves screen in buffer SCRNSAVE.
; When SWAP_SAVE=FF, exchanges contents of screen SCRNSAVE.
; This procedure does no BIOS calls -- all direct writes
;
WIN_SWP_SAV PROC NEAR
;
;******* Setup for processing loop
MOV CX,WNDW_HT ; Number of rows in window area
MOV ES,DISPLAY_SEG ; Load up the video segment
MOV SI,OFFSET SCRNSAVE ; Point SI at beginning of buffer
XOR DI,DI ; DI indexes rows (lines)
XOR BX,BX ; BX indexes columns (chars)
;
;******* Loop on lines
WS050 LABEL NEAR
MOV BL,LEFT_MARG ; Start on this line at left margin
;------- Process next character and attribute on line
WS100: SHL BX,1 ; Change column number to byte pointer
MOV AX,ES:[BX][DI] ; Get the next char/attr from screen
XCHG AX,[SI] ; Store it and write saved char
ADD SI,2 ; Move pointer over a word
;------- Perform swap/save check
CMP SWAP_SAVE,0 ; Is this window swap or window save?
JZ WS150 ; Loop around if a save
MOV ES:[BX][DI],AX ; For swap, restore that char to screen
;------- Test for end of line
WS150: SHR BX,1 ; Put byte offset back to col number
INC BX
CMP BL,RIGHT_MARG ; Are we beyond the end of the line?
JNA WS100 ; No, branch and process next char
;------- Skip down to next line
MOV [SI],CAR_RET2 ; So CRs are fed properly later
ADD SI,2 ; Move pointer over a word
SHL BX,1 ; Adjust DI by one line so that it...
ADD DI,BX ; ...points to beginning of next line
LOOP WS050
;
;******* Exit
RET
WIN_SWP_SAV ENDP
;======= Proc EDITOR
;
; Check for edit keys (such as Ins, Del, etc).
; If not one of those, assume its ASCII and insert it in the screen.
; Return when `cut' hotkey detected.
;
EDITOR PROC NEAR
;
;******* Get a character and prepare for processing
ED100 LABEL NEAR
;------- Read a character from the keyboard
CALL GET_CHAR
;------- If character is ASCII, then null out scan code
OR AL,AL
JZ ED105
XOR AH,AH
;------- Remove previous shading (if any)
ED105: CALL UNSHAD_SCRN
;------- Place cursor position in BX (will become new cursor position)
MOV BX,CURSOR_POS
;------- Check for and loop around if not `cut' hotkey pressed
CMP AX,CUT_KEY
JNZ ED120
;------- `Cut' hotkey pressed -- set termination conditions
MOV MARK,-1
MOV FEED_END1,0
MOV FEED_END2,0
;------- Branch to end of routine
JMP ED800
;
;******* Simple cursor movement keys
;------- Left arrow
ED120: CMP AX,LEFT_ARROW
JNZ ED140
DEC BL
JMP ED500
;------- Right arrow
ED140: CMP AX,RIGHT_ARROW
JNZ ED160
INC BL
JMP ED500
;------- Up arrow
ED160: CMP AX,UP_ARROW
JNZ ED180
DEC BH
JMP ED500
;------- Down arrow
ED180: CMP AX,DOWN_ARROW
JNZ ED185
INC BH
JMP ED500
;------- Home
ED185: CMP AX,HOME_KEY
JNZ ED190
MOV BL,LEFT_MARG
JMP ED500
;------- End
ED190: CMP AX,END_KEY
JNZ ED195
MOV BL,RIGHT_MARG
JMP ED500
;------- PageUp
ED195: CMP AX,TOP_PAGE
JNZ ED200
MOV BH,TOP_MARG
JMP ED500
;------- PageDown
ED200: CMP AX,BOT_PAGE
JNZ ED205
MOV BH,BOT_MARG
JMP ED500
;
;******* Control-RightArrow and Control-LeftArrow
;------- Check for Control-RightArrow and set increment
ED205: CMP AX,CTL_RGT_ARW
JNZ ED210
MOV CL,1 ; Set increment to go forward
JMP ED213
;------- Check for Control-LeftArrow and set increment
ED210: CMP AX,CTL_LFT_ARW
JNZ ED220
CMP BL,LEFT_MARG
JLE ED219
MOV CL,-1 ; Set increment to go backward
DEC BL ; Begin one char to left
;------- Get and save character at current position
ED213: MOV DX,BX ; Use DX for cursor for READ_CHAR
CALL READ_CHAR ; Get char at current position
MOV CH,AL ; Save current char in CH
;------- Move one character location and check if margin violated
ED215: ADD DL,CL ; Move over one character
CMP DL,RIGHT_MARG ; Stop at left/right margins
JGE ED218
CMP DL,LEFT_MARG
JLE ED218
;------- Test for change from space to non-space
CALL READ_CHAR ; Read current character
CMP CH,' ' ; Branch if original...
JNZ ED216 ; ...was not a space
CMP AL,' ' ; Is this a space?
JZ ED215 ; Yes, branch and repeat
JMP ED217 ; No, stop
;------- Test for change from non-space to space
ED216: CMP AL,' ' ; Is this a space?
JNZ ED215 ; No, branch and repeat
;------- If we were going towards left then move to the right by one char
ED217: CMP CL,-1
JNZ ED218
ADD DL,1
;------- Place new cursor position in BX
ED218: MOV BX,DX
;------- Branch to end of key processing loop
ED219: JMP ED500
;
;******* Erase remainder of line
;------- Test for, and go to next case if not, DEL_TO_EOL key
ED220: CMP AX,DEL_TO_EOL
JNZ ED240
;------- Use ERASE_LINE proc to erase rest of line
MOV DX,BX
CALL ERASE_LINE
;------- Branch to end of key processing loop
JMP ED500
;
;******* Backspace
;------- Test for, and go to next case if not, BACKSPACE key
ED240: CMP AX,BACKSPACE
JNZ ED260
;------- Move to left one space
DEC BL
;------- Test and branch if we didn't move off screen
CMP BL,LEFT_MARG
JGE ED250
;------- Put cursor at left margin and go to bottom of processing loop
MOV BL,LEFT_MARG
JMP ED500
;------- Jump to Delete key processing to the actual delete
ED250: JMP ED270
;
;******* Delete key
ED260 LABEL NEAR
;------- Test for and branch if not Del key
CMP AX,DELETE
JNZ ED280
;------- Set CX to number of spaces to right of current position
ED270: MOV CL,RIGHT_MARG
SUB CL,BL
XOR CH,CH
;------- Set DX to current cursor position
MOV DX,BX
;------- Branch if CX is zero
JCXZ ED275
;------- Loop which moves CX characters one space to left
ED272: INC DL
CALL READ_CHAR
DEC DL
CALL WRITE_CHAR
INC DL
LOOP ED272
;------- Place BLANK at current cursor position
ED275: MOV AX,BLANK
CALL WRITE_CHAR
;------- Branch to end of key processing loop
JMP ED500
;
;******* Insert key
ED280 LABEL NEAR
;------- Test for, and go to next case if not, Insert key
CMP AX,CHAR_INS
JNZ ED300
;------- Set DX to right margin of current row
MOV DH,BH
MOV DL,RIGHT_MARG
;------- Set CX to number of spaces to right of current position
MOV CL,DL
SUB CL,BL
XOR CH,CH
;------- Branch if CX is zero
JCXZ ED290
;------- Loop which moves CX characters one space to right
ED285: DEC DL
CALL READ_CHAR
INC DL
CALL WRITE_CHAR
DEC DL
LOOP ED285
;------- Place BLANK at current cursor position
ED290: MOV AX,BLANK
CALL WRITE_CHAR
;------- Branch to end of key processing loop
JMP ED500
;
;******* LINE_INS key
ED300 LABEL NEAR
;------- Test for, and go to next case if not, LINE_INS key
CMP AX,LINE_INS
JNZ ED320
;------- Start at the bottom margin
MOV DH,BOT_MARG
;------- Start at the left margin
ED305: MOV DL,LEFT_MARG
;------- Test for and branch if we're on the current line
CMP DH,BH
JZ ED315
;------- Assign loop counter
XOR CX,CX
MOV CL,RIGHT_MARG
INC CX
;------- Loop to move row of char/attr down
ED310: DEC DH
CALL READ_CHAR ; Get the character
INC DH
CALL WRITE_CHAR ; And put it back one line higher
INC DL ; Move right one character
LOOP ED310
;------- Now move up a line and do it again
DEC DH
JMP ED305
;------- Erase the current line
ED315: CALL ERASE_LINE
;------- Branch to end of key processing loop
JMP ED500
;
;******* LINE_DEL key
ED320 LABEL NEAR
;------- Test for, and go to next case if not, LINE_DEL key
CMP AX,LINE_DEL
JNZ ED340
;------- Start at the left margin
ED325: MOV DL,LEFT_MARG
;------- Test for and branch if we're on the last line
CMP DH,BOT_MARG
JZ ED335
;------- Assign loop counter
XOR CX,CX
MOV CL,RIGHT_MARG
INC CX
;------- Loop to move row of char/attr up
ED330: INC DH
CALL READ_CHAR ; Get the character
DEC DH
CALL WRITE_CHAR ; And put it back one line lower
INC DL ; Move right one character
LOOP ED330
;------- Now move down a line and do it again
INC DH
JMP ED325
;------- Erase bottom line on display
ED335: CALL ERASE_LINE ; And wipe out the bottom line
;------- Branch to end of key processing loop
JMP ED500
;
;******* First MARK_CHAR
ED340 LABEL NEAR
;------- Test for, and go to next case if not, `mark' hotkey
CMP AX,MARK_CHAR
JNZ ED400
;------- Put cursor position in DX
MOV DX,BX
;------- Test for and loop around if already marking
CMP MARK,-1
JNZ ED350
;------- Save cursor location in MARK
MOV MARK,BX
;------- Branch to end of key processing loop
JMP ED500
;
;******* Second MARK_CHAR
;------- Store current location as FEED_END1 address in SCRNSAVE buffer
ED350: CALL CONVERT_LOC
MOV FEED_END1,AX
;------- Store previous mark location as feed end address SCRNSAVE buffer
MOV DX,MARK
CALL CONVERT_LOC
MOV FEED_END2,AX
;------- Ensure that FEED_END1 is less than or equal to FEED_END2
MOV CX,FEED_END1
CMP CX,AX
JNA ED360
MOV FEED_END1,AX
MOV FEED_END2,CX
;------- Increase FEED_END2 by one cell
ED360: MOV AX,FEED_END2
ADD AX,2
MOV FEED_END2,AX
;------- Trim unmarked chars from screen
CALL TRIM_SCREEN
;------- Branch to exit routine
JMP ED800
;
;******* ASCII char -- write at current position
ED400: MOV AH,ATTR
CALL W_CHAR
INC BL ; Move over one position
;
;******* Adjust cursor position
;------- Right margin
ED500: CMP BL,RIGHT_MARG
JLE ED520
MOV BL,RIGHT_MARG
;------- Left margin
ED520: CMP BL,LEFT_MARG
JGE ED540
MOV BL,LEFT_MARG
;------- Top margin
ED540: CMP BH,TOP_MARG
JGE ED560
MOV BH,TOP_MARG
;------- Bottom margin
ED560: CMP BH,BOT_MARG
JLE ED600
MOV BH,BOT_MARG
;------- Save new cursor position in CURSOR_POS
ED600: MOV CURSOR_POS,BX
;------- Move cursor to the new position
MOV DX,BX
CALL PUT_CURSOR
;------- Jump to beginning of processing loop
JMP ED100
;
;******* End routine
ED800: RET
;
EDITOR ENDP
;======= Proc PUT_CURSOR
;
; Place cursor at location in DX
;
PUT_CURSOR PROC NEAR
;------- If we are in mark mode, set the screen square to inverse video
CALL SHADE_SCRN
MOV AH,02H ; SET_CUR_POS service
PUSH BX
XOR BX,BX ; Page 0
INT 10H
POP BX
RET
PUT_CURSOR ENDP
UP_LEFT LABEL WORD
ULHC_C DB 0
ULHC_R DB 0
LW_RIGHT LABEL WORD
LRHC_C DB 0
LRHC_R DB 0
ATTRIB DB 0
SAVE_AX DW 0
SAVE_DX DW 0
;======= Proc SHADE_SCRN
;
; Assigns INV_ATTR to characters in box defined by
; MARK and CURSOR_POS. Called by PUT_CURSOR.
;
SHADE_SCRN PROC NEAR
;
;******* Initialization
;------- Test for, and branch to routine end, if not marking
CMP MARK,-1 ; Is a mark set?
JZ SS450
;------- Save AX and DX registers
MOV SAVE_AX,AX
MOV SAVE_DX,DX
;------- First set the attribute to inverse
MOV ATTRIB,INV_ATTR
;
;******* Place upper left-hand corner of shaded box in UP_LEFT
;------- Place current position in BX and marked position in AX
MOV BX,CURSOR_POS
MOV AX,MARK
;------- Place upper left-hand corner column value in AL
CMP BL,AL
JA SS100
MOV AL,BL
;------- Place upper left-hand corner row value in AH
SS100: CMP BH,AH
JA SS200
MOV AH,BH
;------- Store upper left-hand corner in UP_LEFT
SS200: MOV UP_LEFT,AX
;
;******* Place lower right-hand corner of shaded box in LW_RIGHT
;------- Place marked position in AX
MOV AX,MARK
;------- Place column value in AL
CMP BL,AL
JBE SS300
MOV AL,BL
;------- Place row value in AH
SS300: CMP BH,AH
JBE SS400
MOV AH,BH
;------- Store LRH corner in LW_RIGHT
SS400: MOV LW_RIGHT,AX
;
;******* Call SS_COMMON to do the shading
CALL SS_COMMON
;
;******* Return to caller
SS450: RET
SHADE_SCRN ENDP
;======= Proc UNSHAD_SCRN
;
; Assign ATTR to attributes of characters in the box defined
; by UP_LEFT and LW_RIGHT. Called by EDITOR, at the beginning
; of key processing.
;
UNSHAD_SCRN PROC NEAR
;
;------- Test for, and branch to routine end, if not marking
CMP MARK,-1 ; Here we unshade the shaded area
JZ SS475
;------- Save AX and DX registers
MOV SAVE_AX,AX
MOV SAVE_DX,DX
;------- Set the attribute to normal
MOV ATTRIB,ATTR ; Change the box back to normal
;------- Call SS_COMMON to do the shading
CALL SS_COMMON
;------- Return to caller
SS475: RET
;
UNSHAD_SCRN ENDP
;======= Proc SS_COMMON
;
; Assign ATTRIB to attributes of characters in the box defined
; by UP_LEFT and LW_RIGHT.
;
SS_COMMON PROC NEAR
;
;******* Initialization
;------- Put box corners in BX and DX registers
MOV DX,UP_LEFT ; DX is variable -- loc to change attr
MOV BX,LW_RIGHT ; BX is constant
;
;******* Change attributes of row in DH
;------- Test and branch if past last column
SS500: CMP DL,BL
JA SS600
;------- Change the attribute
MOV AH,ATTRIB
CALL WRITE_ATTR
;------- Point to next char on row and branch to top
INC DL
JMP SS500
;
;******* Prepare to do next row
;------- Point to next row
SS600: INC DH
;------- Branch if past last row
CMP DH,BH
JA SS700
;------- Put first column in DL and branch
MOV DL,ULHC_C
JMP SS500
;
;******* End program
;------- Restore AX and DX
SS700: MOV AX,SAVE_AX
MOV DX,SAVE_DX
;------- Return
RET
;
SS_COMMON ENDP
;======= Proc TRIM_SCREEN
;
; Trims the right margin of screen after the second mark
;
TRIM_SCREEN PROC NEAR
;
;******* Initialization
;------- First clear mark
MOV MARK,-1
;------- Test and branch to return if at right-hand edge of screen
MOV DL,LRHC_C
CMP DL,RIGHT_MARG
JZ TS200
;------- Put space to right of upper right-hand corner in BX
MOV BH,ULHC_R
MOV BL,LRHC_C
INC BL
;******* Loop to erase to right of box
;------- Place lower right-hand corner in DX
TS100: MOV DX,LW_RIGHT
;------- Branch to end if BH equals DH
CMP BH,DH
JZ TS200
;------- Erase end of current line
MOV DX,BX
CALL ERASE_LINE
;------- Increment current line and repeat
INC BH
JMP TS100
;
;******* End routine
TS200: RET
TRIM_SCREEN ENDP
;======= Proc TRIM_BUF
;
; Trims blanks from first and last row in the feed area
;
TRIM_BUF PROC NEAR
;
;******* Exit unless buffer holds data
MOV BX,FEED_END2
CMP BX,FEED_END1
JZ TB070
;
;******* Trim trailing blanks off last row
;------- Set CX to number of chars in a row
XOR CX,CX
MOV CL,LRHC_C
SUB CL,ULHC_C
INC CX
;------- Move backwards until first non-blank is found
TB010: MOV AX,[BX-2]
CMP AL,' '
JNZ TB020
SUB BX,2
LOOP TB010
;------- Reassign FEED_END2
TB020: MOV FEED_END2,BX
;
;******* Check for, and skip over, a blank first line
;------- Branch and end if feed area corresponds to one row
MOV AX,UP_LEFT
CMP AH,LRHC_R
JZ TB070
;------- Set CX to number of chars in a row
XOR CX,CX
MOV CL,LRHC_C
SUB CL,ULHC_C
INC CX
;------- Point BX to start of feed area
MOV BX,FEED_END1
;------- Check for, and branch to end, if a non-blank char is found
TB030: MOV AX,[BX]
CMP AL,' '
JNZ TB070
ADD BX,2
LOOP TB030
;------- Set AX to number of chars from left side to carriage return
XOR AX,AX
MOV AL,RIGHT_MARG
INC AL
SUB AL,ULHC_C
;------- Convert to words and add to FEED_END1
SHL AX,1
ADD FEED_END1,AX
;******* End routine
TB070: RET
;
TRIM_BUF ENDP
;======= Proc READ_CURSOR
;
; Find the current cursor location and place in DX
;
READ_CURSOR PROC NEAR
MOV AH,03H
PUSH BX
XOR BX,BX
INT 10H
POP BX
RET
READ_CURSOR ENDP
;======= Proc READ_CHAR
;
; Read character on screen at location in DX and place in AX
;
READ_CHAR PROC NEAR
;------- Covert the row and column into location
CALL CAL_VID_LOC
;------- Get the character and attribute at that location
MOV AX,ES:[DI]
RET
READ_CHAR ENDP
;======= Proc R_CHAR
;
; Read character on screen at current cursor location and place in AX
;
R_CHAR PROC NEAR
MOV AH,08H
PUSH BX
XOR BH,BH
INT 10H
POP BX
RET
R_CHAR ENDP
;======= Proc WRITE_CHAR
;
; Write character and attribute in AX to screen at location in DX
;
WRITE_CHAR PROC NEAR
PUSH AX ; Save the attrib from destruction
CALL CAL_VID_LOC ; Convert the row and col into location
POP AX
MOV ES:[DI],AX
RET
WRITE_CHAR ENDP
;======= Proc WRITE_ATTR
;
; Write attrib in AX to screen location in DX
;
WRITE_ATTR PROC NEAR
PUSH AX
CALL CAL_VID_LOC
POP AX
MOV ES:[DI+1],AH
RET
WRITE_ATTR ENDP
;======= Proc W_CHAR
;
; Write character and attribute in AX at current cursor location
;
W_CHAR PROC NEAR
;------- Save registers
PUSH BX
PUSH CX
;------- Do the write using INT 10H
MOV BL,AH ; Place attribute in BL
XOR BH,BH ; Page 0
MOV CX,1 ; Count = 1
MOV AH,09H ; WRITE_CHAR_ATTR service
INT 10H
;------- Restore registers
POP CX
POP BX
;------- Exit
RET
W_CHAR ENDP
;======= Proc GET_CHAR
;
; Get character from keyboard into AX
;
GET_CHAR PROC NEAR
MOV AH,0
PUSHF
CALL OLDINT16
RET
GET_CHAR ENDP
;======= Proc ERASE_LINE
;
; Erase the current line on the screen from DX to RIGHT_MARG, inclusive
;
ERASE_LINE PROC NEAR
;------- Set CX to number of columns to erase
XOR CX,CX
MOV CL,RIGHT_MARG
SUB CL,DL
INC CL
;------- Loop to blank row
ER100: MOV AX,BLANK
CALL WRITE_CHAR
INC DL
LOOP ER100
;------- Exit routine
RET
ERASE_LINE ENDP
;======= Proc CONVERT_LOC
;
; Convert a location on the screen (in DX) into an offset in
; the SCRNSAVE buffer (in AX).
;
CONVERT_LOC PROC NEAR
;
;******* Initialize
;------- Place line length (in bytes) in BX
XOR BX,BX
MOV BL,RIGHT_MARG
INC BX
INC BX
SHL BX,1
;------- Place location in CX
MOV CX,DX
;------- Point AX at beginning of SCRNSAVE buffer
MOV AX,OFFSET SCRNSAVE
;
;******* Loop to find row
;------- Test and branch if CH equals top row of window
CL100: CMP CH,TOP_MARG
JZ CL200
;------- Add a row to AX and repeat test
ADD AX,BX
DEC CH
JMP CL100
;
;******* Loop to find column
;------- Test and branch if CL equals left margin of window
CL200: CMP CL,LEFT_MARG
JZ CL300
;------- Add a column and repeat
ADD AX,2
DEC CL
JMP CL200
;
;******* Exit
CL300: RET
CONVERT_LOC ENDP
;======= Proc CAL_VID_LOC
;
; Convert the screen location (row and column) in DX into an
; offset into the video display buffer in DI
;
CAL_VID_LOC PROC NEAR
;------- Initialize AX, CX, DI to zero
PUSH CX ; Save CX -- some callers need it
XOR AX,AX ; AX holds line length
XOR CX,CX ; CX is temp for location
XOR DI,DI ; DI holds offset
;------- Set AX to line length in chars
MOV AL,RIGHT_MARG
INC AX
;------- Set CX to row number and branch if zero
MOV CL,DH ; Put the number of rows into cx
JCXZ CVL200
;------- Increment DI by the line length times the row number
CVL100: ADD DI,AX
LOOP CVL100
;------- Increment DI by the column number
CVL200: MOV AL,DL
ADD DI,AX
;------- End routine
POP CX ; Restore CX
SHL DI,1 ; Now convert this into byte offset
MOV ES,DISPLAY_SEG
RET
CAL_VID_LOC ENDP
;-------------------------------
;======= Replacement for INT 16H
;-------------------------------
;
ASSUME CS:COMSEG, DS:NOTHING, ES:NOTHING
BEGIN PROC FAR
;
;======= Initial processing -- figure out what to do
;
;******* Check if in middle of feeding characters
MAINLOOP LABEL NEAR
;------- Save caller's AX register
MOV REQUEST,AX
AND AH,0EFH
MOV REQUEST2,AH
;------- Branch if we're in the middle of feeding characters
MOV AX,FEED_START ; Are we in the middle of feeding...
CMP AX,FEED_STOP ; ...chars to the application?
JZ NOFEED ; No, loop around
JMP FEED ; Yes, branch
;
;******* Test for type-0 request and return if not
NOFEED LABEL NEAR
;------- Restore caller's AX
MOV AX,REQUEST
;------- Check for type-0 request -- only interested in char requests
CMP REQUEST2,0
JZ CONTINU
;------- Jump to old INT 16 routine, which will return to application
JMP OLDINT16
;
;******* Now check for a hot key -- return if not
CONTINU LABEL NEAR
;------- Call old INT 16H (emulate interrupt)
PUSHF
CALL OLDINT16
;------- Check for Alt-Fn9
CMP AX,CUT_KEY
JZ GOT_CUT_KEY
;------- Check for Ctl-Fn9
CMP AX,PASTE_KEY
JZ PASTE_CHAR
;------- Not a hotkey -- provide keystroke to caller
IRET
;
;======= `Cut' hotkey processing
;
;******* Setup for subroutine calls
GOT_CUT_KEY LABEL NEAR
;------- Save stack and point SS:SP to top of PSP
MOV SAVSTAKSS,SS
MOV SAVSTAKSP,SP
MOV AX,CS
MOV SS,AX
MOV SP,100H
;------- Enable interrupts while processing characters
STI
;------- Set up a stack frame
PUSH BP
MOV BP,SP
;------- Save registers
SUB SP,0EH
CALL SAVEREG
;------- Set DS to COMSEG
MOV DS,AX
ASSUME DS:COMSEG
;
;******* Call EDITOR and other subroutines
;------- Calculate window extremeties
CALL CALC_WINDOW
;------- Save the cursor for later restoring
CALL READ_CURSOR
MOV OLD_CUR_POS,DX
;------- Save screen in buffer SCRNSAVE
CALL WINDOW_SAVE
;------- Restore cursor in edit window
MOV DX,CURSOR_POS
CALL PUT_CURSOR
;------- Edit in the window
CALL EDITOR
;------- Save cursor in edit window for next edit
CALL READ_CURSOR
MOV CURSOR_POS,DX
;------- Put back whatever was originally there
CALL WINDOW_SWAP
;------- Trim blanks from end of buffer
CALL TRIM_BUF
;------- Restore the cursor position
MOV DX,OLD_CUR_POS
CALL PUT_CURSOR
;
;******* End `cut-key' processing
;------- Restore registers
CALL RESTREG
ADD SP,0EH
ASSUME DS:NOTHING
;------- Restore stack
POP BP
MOV SS,SAVSTAKSS
MOV SP,SAVSTAKSP
;------- End of loop -- jump to beginning
MOV AX,REQUEST ; Restore request
JMP MAINLOOP ; Get another char to return to caller
;
;======= `Paste' hotkey processing
;
;******* Paste characters into application
PASTE_CHAR LABEL NEAR
MOV AX,FEED_END1
MOV FEED_START,AX
MOV AX,FEED_END2
MOV FEED_STOP,AX
MOV AX,REQUEST ; Restore request
JMP MAINLOOP ; Go get another character to return
;
;******* Feed characters to application from the buffer SCRNSAVE
FEED LABEL NEAR
;------- Test and branch for return if a type-2 request
CMP REQUEST2,1
JA KSTAT
;------- Get next char from SCRNSAVE buffer into AX
PUSH BX
MOV BX,AX
MOV AX,CS:[BX]
POP BX
;------- Loop around if end of screen line
CMP AX,CAR_RET2
JZ FEED10
;------- Null attribute
XOR AH,AH
;------- Save in FEED_CHAR
FEED10: MOV FEED_CHAR,AX
;------- Test and branch if type-1 request
CMP REQUEST2,0
JNZ FEED_STAT
;------- Point to next character
ADD FEED_START,2
;
;******* Handle beginning and end of line
;------- Test for and loop around if not carriage return
CMP AL,CAR_RET1
JNZ FEED20
;------- Skip to beginning of the feed area
PUSH DX
XOR DX,DX
MOV DL,ULHC_C
SHL DX,1 ; In words
ADD FEED_START,DX
POP DX
;------- Put address of next buffer location in BX
FEED20: PUSH BX
MOV BX,FEED_START
;------- Test, and branch to return, if at end of feed area
FEED30: CMP BX,FEED_STOP
JZ FEED50
;------- Test and loop around if next char is space
MOV AX,CS:[BX]
CMP AL,' '
JZ FEED40
;------- Test and branch if a non-space char follows on this line
CMP AX,CAR_RET2
JNZ FEED50
;------- Point to carriage return, skipping over trailing blanks
MOV FEED_START,BX
JMP FEED50
;------- Point to next char on line and branch to top of loop
FEED40: INC BX
INC BX
JMP FEED30
;
;******* Exit to caller
;------- Exit for type-0 request -- returning character to application
FEED50 LABEL NEAR
POP BX ; Restore stack
MOV AX,FEED_CHAR ; Place char to feed in AX
IRET ; Return char to application
;------- Exit for type-1 request: enable interrupts, feed ZF = NZ and char
FEED_STAT LABEL NEAR
STI
RET 02
;------- Exit for type-2 request -- invoke INT 16H to do what he wants
KSTAT: MOV AX,REQUEST
JMP OLDINT16
BEGIN ENDP
;======= Proc SAVEREG
;------- Save registers on stack frame
SAVEREG PROC NEAR
MOV [BP-2],BX
MOV [BP-4],CX
MOV [BP-6],DX
MOV [BP-8],SI
MOV [BP-0AH],DI
MOV [BP-0CH],DS
MOV [BP-0EH],ES
RET
SAVEREG ENDP
;======= Proc RESTREG
;------- Now put the registers back
RESTREG PROC NEAR
MOV BX,[BP-2]
MOV CX,[BP-4]
MOV DX,[BP-6]
MOV SI,[BP-8]
MOV DI,[BP-0AH]
MOV DS,[BP-0CH]
MOV ES,[BP-0EH]
RET
RESTREG ENDP
;------- Upper limit resident program (paras)
RESPARA EQU ($-START+100H+15)/16
;-------------------------
;======= Installation code
;-------------------------
INSTALL LABEL NEAR
ASSUME CS:COMSEG, DS:COMSEG, ES:COMSEG
;------- Output 'OK' message
MOV DX,OFFSET MESSAGE
MOV AH,09H
INT 21H
;------- Get existing 16H vector using ES:BX
MOV AX,3516H
INT 21H
MOV OLDINT16IP,BX
MOV OLDINT16CS,ES
ASSUME ES:NOTHING
;------- Now put our routine there using DS:DX
MOV AX,2516H
MOV DX,OFFSET BEGIN
INT 21H
;------- Terminate and stay resident
MOV DX,RESPARA
MOV AX,3100H
INT 21H
MESSAGE DB 10,13,'CUTPASTE installed',10,13
DB ' Alt-Fn9 to enable cut ',10,13
DB ' Start/stop marking: Fn1',10,13
DB ' Move 1 space (4 ways): Cursor keys', 10,13
DB ' Move 1 word (2 ways): Ctl-LeftArw, Ctl-RgtArw', 10,13
DB ' Move to screen edge (4 ways): Home, End, PgUp, PgDn', 10,13
DB ' Erase to End of Line: Fn6', 10,13
DB ' Erase Line: Fn7', 10,13
DB ' Add Line: Enter', 10,13
DB ' Move text to right/left: Ins, Del, Bksp', 10,13
DB ' Ctl-Fn9 to paste',10,13,'$'
COMSEG ENDS
END START